import type { SelfInfo } from 'napcat-core/index';

import { NodeIKernelLoginListener, NodeIKernelSessionListener } from 'napcat-core/listeners';
import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from 'napcat-core/adapters';
import { NapCatPathWrapper } from 'napcat-common/src/path';
import {
  genSessionConfig,
  InstanceContext,
  loadQQWrapper,
  NapCatCore,
  NapCatCoreWorkingEnv,
  NodeIQQNTStartupSessionWrapper,
  NodeIQQNTWrapperEngine,
  NodeIQQNTWrapperSession,
  PlatformType,
  WrapperNodeApi,
  WrapperSessionInitConfig,
} from 'napcat-core';
import { hostname, systemVersion } from 'napcat-common/src/system';
import path from 'path';
import fs from 'fs';
import os from 'os';
import { LoginListItem, NodeIKernelLoginService } from 'napcat-core/services';
import qrcode from 'napcat-qrcode/lib/main';
import { NapCatOneBot11Adapter } from 'napcat-onebot/index';
import { InitWebUi } from 'napcat-webui-backend/index';
import { WebUiDataRuntime } from 'napcat-webui-backend/src/helper/Data';
import { napCatVersion } from 'napcat-common/src/version';
import { NodeIO3MiscListener } from 'napcat-core/listeners/NodeIO3MiscListener';
import { sleep } from 'napcat-common/src/helper';
import { FFmpegService } from '@/napcat-core/helper/ffmpeg/ffmpeg';
import { connectToNamedPipe } from './pipe';
import { NativePacketHandler } from 'napcat-core/packet/handler/client';
import { logSubscription, LogWrapper } from '@/napcat-core/helper/log';
import { proxiedListenerOf } from '@/napcat-core/helper/proxy-handler';
import { QQBasicInfoWrapper } from '@/napcat-core/helper/qq-basic-info';
import { statusHelperSubscription } from '@/napcat-core/helper/status';
import { applyPendingUpdates } from '@/napcat-webui-backend/src/api/UpdateNapCat';
// NapCat Shell App ES 入口文件
async function handleUncaughtExceptions (logger: LogWrapper) {
  process.on('uncaughtException', (err) => {
    logger.logError('[NapCat] [Error] Unhandled Exception:', err.message);
  });
  process.on('unhandledRejection', (reason) => {
    logger.logError('[NapCat] [Error] unhandledRejection:', reason);
  });
}

function getDataPaths (wrapper: WrapperNodeApi): [string, string] {
  if (os.platform() === 'darwin') {
    const userPath = os.homedir();
    const appDataPath = path.resolve(userPath, './Library/Application Support/QQ');
    return [appDataPath, path.join(appDataPath, 'global')];
  }
  let dataPath = wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig();
  if (!dataPath) {
    dataPath = path.resolve(os.homedir(), './.config/QQ');
    fs.mkdirSync(dataPath, { recursive: true });
  }
  const dataPathGlobal = path.resolve(dataPath, './nt_qq/global');
  return [dataPath, dataPathGlobal];
}

function getPlatformType (): PlatformType {
  const platformMapping: Partial<Record<NodeJS.Platform, PlatformType>> = {
    win32: PlatformType.KWINDOWS,
    darwin: PlatformType.KMAC,
    linux: PlatformType.KLINUX,
  };
  return platformMapping[os.platform()] ?? PlatformType.KWINDOWS;
}

async function initializeEngine (
  engine: NodeIQQNTWrapperEngine,
  basicInfoWrapper: QQBasicInfoWrapper,
  dataPathGlobal: string,
  systemPlatform: PlatformType,
  systemVersion: string
) {
  engine.initWithDeskTopConfig(
    {
      base_path_prefix: '',
      platform_type: systemPlatform,
      app_type: 4,
      app_version: basicInfoWrapper.getFullQQVersion(),
      os_version: systemVersion,
      use_xlog: false,
      qua: basicInfoWrapper.QQVersionQua ?? '',
      global_path_config: {
        desktopGlobalPath: dataPathGlobal,
      },
      thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 },
    },
    new NodeIGlobalAdapter()
  );
}

async function initializeLoginService (
  loginService: NodeIKernelLoginService,
  basicInfoWrapper: QQBasicInfoWrapper,
  dataPathGlobal: string,
  systemVersion: string,
  hostname: string
) {
  loginService.initConfig({
    machineId: '',
    appid: basicInfoWrapper.QQVersionAppid ?? '',
    platVer: systemVersion,
    commonPath: dataPathGlobal,
    clientVer: basicInfoWrapper.getFullQQVersion(),
    hostName: hostname,
  });
}

async function handleLogin (
  loginService: NodeIKernelLoginService,
  logger: LogWrapper,
  pathWrapper: NapCatPathWrapper,
  quickLoginUin: string | undefined,
  historyLoginList: LoginListItem[]
): Promise<SelfInfo> {
  const context = { isLogined: false };
  let inner_resolve: (value: SelfInfo) => void;
  const selfInfo: Promise<SelfInfo> = new Promise((resolve) => {
    inner_resolve = resolve;
  });
  // 连接服务

  const loginListener = new NodeIKernelLoginListener();
  loginListener.onUserLoggedIn = (userid: string) => {
    logger.logError(`当前账号(${userid})已登录,无法重复登录`);
  };
  loginListener.onQRCodeLoginSucceed = async (loginResult) => {
    context.isLogined = true;
    inner_resolve({
      uid: loginResult.uid,
      uin: loginResult.uin,
      nick: '',
      online: true,
    });
  };
  loginListener.onLoginConnected = () => {
    waitForNetworkConnection(loginService, logger).then(() => {
      handleLoginInner(context, logger, loginService, quickLoginUin, historyLoginList).then().catch(e => logger.logError(e));
      loginListener.onLoginConnected = () => { };
    });
  };
  loginListener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => {
    WebUiDataRuntime.setQQLoginQrcodeURL(qrcodeUrl);

    const realBase64 = pngBase64QrcodeData.replace(/^data:image\/\w+;base64,/, '');
    const buffer = Buffer.from(realBase64, 'base64');
    logger.logWarn('请扫描下面的二维码，然后在手Q上授权登录：');
    const qrcodePath = path.join(pathWrapper.cachePath, 'qrcode.png');
    qrcode.generate(qrcodeUrl, { small: true }, (res) => {
      logger.logWarn([
        '\n',
        res,
        '二维码解码URL: ' + qrcodeUrl,
        '如果控制台二维码无法扫码，可以复制解码url到二维码生成网站生成二维码再扫码，也可以打开下方的二维码路径图片进行扫码。',
      ].join('\n'));
      fs.writeFile(qrcodePath, buffer, {}, () => {
        logger.logWarn('二维码已保存到', qrcodePath);
      });
    });
  };

  loginListener.onQRCodeSessionFailed = (errType: number, errCode: number) => {
    if (!context.isLogined) {
      logger.logError('[Core] [Login] Login Error,ErrType: ', errType, ' ErrCode:', errCode);
      if (errType === 1 && errCode === 3) {
        // 二维码过期刷新
      }
      loginService.getQRCodePicture();
    }
  };

  loginListener.onLoginFailed = (...args) => {
    logger.logError('[Core] [Login] Login Error , ErrInfo: ', JSON.stringify(args));
  };

  loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger));
  loginService.connect();
  return await selfInfo;
}
async function handleLoginInner (context: { isLogined: boolean; }, logger: LogWrapper, loginService: NodeIKernelLoginService, quickLoginUin: string | undefined, historyLoginList: LoginListItem[]) {
  WebUiDataRuntime.setQuickLoginCall(async (uin: string) => {
    return await new Promise((resolve) => {
      if (uin) {
        logger.log('正在快速登录 ', uin);
        loginService.quickLoginWithUin(uin).then(res => {
          if (res.loginErrorInfo.errMsg) {
            resolve({ result: false, message: res.loginErrorInfo.errMsg });
          }
          resolve({ result: true, message: '' });
        }).catch((e) => {
          logger.logError(e);
          resolve({ result: false, message: '快速登录发生错误' });
        });
      } else {
        resolve({ result: false, message: '快速登录失败' });
      }
    });
  });
  if (quickLoginUin) {
    if (historyLoginList.some(u => u.uin === quickLoginUin)) {
      logger.log('正在快速登录 ', quickLoginUin);
      loginService.quickLoginWithUin(quickLoginUin)
        .then(result => {
          if (result.loginErrorInfo.errMsg) {
            logger.logError('快速登录错误：', result.loginErrorInfo.errMsg);
            if (!context.isLogined) loginService.getQRCodePicture();
          }
        })
        .catch();
    } else {
      logger.logError('快速登录失败，未找到该 QQ 历史登录记录，将使用二维码登录方式');
      if (!context.isLogined) loginService.getQRCodePicture();
    }
  } else {
    logger.log('没有 -q 指令指定快速登录，将使用二维码登录方式');
    if (historyLoginList.length > 0) {
      logger.log(`可用于快速登录的 QQ：\n${historyLoginList
        .map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`)
        .join('\n')
        }`);
    }
    loginService.getQRCodePicture();
    try {
      await WebUiDataRuntime.runWebUiConfigQuickFunction();
    } catch (error) {
      logger.logError('WebUi 快速登录失败 执行失败', error);
    }
  }

  loginService.getLoginList().then((res) => {
    // 遍历 res.LocalLoginInfoList[x].isQuickLogin是否可以 res.LocalLoginInfoList[x].uin 转为string 加入string[] 最后遍历完成调用WebUiDataRuntime.setQQQuickLoginList
    const list = res.LocalLoginInfoList.filter((item) => item.isQuickLogin);
    WebUiDataRuntime.setQQQuickLoginList(list.map((item) => item.uin.toString()));
    WebUiDataRuntime.setQQNewLoginList(list);
  });
}

async function initializeSession (
  session: NodeIQQNTWrapperSession,
  sessionConfig: WrapperSessionInitConfig,
  startupSession: NodeIQQNTStartupSessionWrapper | null
) {
  return new Promise<void>((resolve, reject) => {
    const sessionListener = new NodeIKernelSessionListener();
    sessionListener.onOpentelemetryInit = (info) => {
      if (info.is_init) {
        resolve();
      } else {
        reject(new Error('opentelemetry init failed'));
      }
    };
    session.init(
      sessionConfig,
      new NodeIDependsAdapter(),
      new NodeIDispatcherAdapter(),
      sessionListener
    );
    if (startupSession) {
      startupSession.start();
    } else {
      try {
        session.startNT(0);
      } catch {
        try {
          session.startNT();
        } catch (e: unknown) {
          reject(new Error('init failed ' + (e as Error).message));
        }
      }
    }
  });
}
async function handleProxy (session: NodeIQQNTWrapperSession, logger: LogWrapper) {
  if (process.env['NAPCAT_PROXY_PORT']) {
    session.getMSFService().setNetworkProxy({
      userName: '',
      userPwd: '',
      address: process.env['NAPCAT_PROXY_ADDRESS'] || '127.0.0.1',
      port: +process.env['NAPCAT_PROXY_PORT'],
      proxyType: 2,
      domain: '',
      isSocket: true,
    });
    logger.logWarn('已设置代理', process.env['NAPCAT_PROXY_ADDRESS'], process.env['NAPCAT_PROXY_PORT']);
  } else if (process.env['NAPCAT_PROXY_CLOSE']) {
    session.getMSFService().setNetworkProxy({
      userName: '',
      userPwd: '',
      address: '',
      port: 0,
      proxyType: 0,
      domain: '',
      isSocket: false,
    });
  }
}

async function waitForNetworkConnection (loginService: NodeIKernelLoginService, logger: LogWrapper) {
  let network_ok = false;
  let _tryCount = 0;
  while (!network_ok) {
    network_ok = loginService.getMsfStatus() !== 3;// win 11 0连接 1未连接
    logger.log('等待网络连接...');
    await sleep(500);
    _tryCount++;
  }
  logger.log('网络已连接');
  return network_ok;
}

export async function NCoreInitShell () {
  console.log('NapCat Shell App Loading...');
  const pathWrapper = new NapCatPathWrapper();
  const logger = new LogWrapper(pathWrapper.logsPath);
  handleUncaughtExceptions(logger);
  await applyPendingUpdates(pathWrapper);

  // 初始化 FFmpeg 服务
  await FFmpegService.init(pathWrapper.binaryPath, logger);

  if (process.env['NAPCAT_DISABLE_PIPE'] !== '1') {
    await connectToNamedPipe(logger).catch(e => logger.logError('命名管道连接失败', e));
  }
  const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
  const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVersion());
  const nativePacketHandler = new NativePacketHandler({ logger }); // 初始化 NativePacketHandler 用于后续使用

  // nativePacketHandler.onAll((packet) => {
  //     console.log('[Packet]', packet.uin, packet.cmd, packet.hex_data);
  // });
  await nativePacketHandler.init(basicInfoWrapper.getFullQQVersion());

  const o3Service = wrapper.NodeIO3MiscService.get();
  o3Service.addO3MiscListener(new NodeIO3MiscListener());

  logger.log('[NapCat] [Core] NapCat.Core Version: ' + napCatVersion);
  WebUiDataRuntime.setWorkingEnv(NapCatCoreWorkingEnv.Shell);
  InitWebUi(logger, pathWrapper, logSubscription, statusHelperSubscription).then().catch(e => logger.logError(e));
  const engine = wrapper.NodeIQQNTWrapperEngine.get();
  const loginService = wrapper.NodeIKernelLoginService.get();
  let session: NodeIQQNTWrapperSession;
  let startupSession: NodeIQQNTStartupSessionWrapper | null = null;
  try {
    startupSession = wrapper.NodeIQQNTStartupSessionWrapper.create();
    session = wrapper.NodeIQQNTWrapperSession.getNTWrapperSession('nt_1');
  } catch (e: unknown) {
    try {
      session = wrapper.NodeIQQNTWrapperSession.create();
    } catch (error) {
      logger.logError('创建 StartupSession 失败', e);
      logger.logError('创建 Session 失败', error);
      throw error;
    }
  }
  const [dataPath, dataPathGlobal] = getDataPaths(wrapper);
  const systemPlatform = getPlatformType();

  if (!basicInfoWrapper.QQVersionAppid || !basicInfoWrapper.QQVersionQua) throw new Error('QQVersionAppid or QQVersionQua  is not defined');

  await initializeEngine(engine, basicInfoWrapper, dataPathGlobal, systemPlatform, systemVersion);
  await initializeLoginService(loginService, basicInfoWrapper, dataPathGlobal, systemVersion, hostname);
  handleProxy(session, logger);

  let quickLoginUin: string | undefined;
  try {
    const args = process.argv;
    const qIndex = args.findIndex(arg => arg === '-q' || arg === '--qq');
    if (qIndex !== -1 && qIndex + 1 < args.length) {
      quickLoginUin = args[qIndex + 1];
    }
  } catch (error) {
    logger.logWarn('解析命令行参数失败，无法使用快速登录功能', error);
  }

  const historyLoginList = (await loginService.getLoginList()).LocalLoginInfoList;

  const dataTimestape = new Date().getTime().toString();
  o3Service.reportAmgomWeather('login', 'a1', [dataTimestape, '0', '0']);

  const selfInfo = await handleLogin(loginService, logger, pathWrapper, quickLoginUin, historyLoginList);
  const amgomDataPiece = 'eb1fd6ac257461580dc7438eb099f23aae04ca679f4d88f53072dc56e3bb1129';
  o3Service.setAmgomDataPiece(basicInfoWrapper.QQVersionAppid, new Uint8Array(Buffer.from(amgomDataPiece, 'hex')));

  let guid = loginService.getMachineGuid();
  guid = guid.slice(0, 8) + '-' + guid.slice(8, 12) + '-' + guid.slice(12, 16) + '-' + guid.slice(16, 20) + '-' + guid.slice(20);
  o3Service.reportAmgomWeather('login', 'a6', [dataTimestape, '184', '329']);

  const sessionConfig = await genSessionConfig(
    guid,
    basicInfoWrapper.QQVersionAppid,
    basicInfoWrapper.getFullQQVersion(),
    selfInfo.uin,
    selfInfo.uid,
    dataPath
  );

  await initializeSession(session, sessionConfig, startupSession);

  const accountDataPath = path.resolve(dataPath, './NapCat/data');
  // 判断dataPath是否为根目录 或者 D:/ 之类的盘目录
  if (dataPath !== '/' && /^[a-zA-Z]:\\$/.test(dataPath) === false) {
    try {
      fs.mkdirSync(accountDataPath, { recursive: true });
    } catch (error) {
      logger.logError('创建accountDataPath失败', error);
    }
  }

  logger.logDebug('本账号数据/缓存目录：', accountDataPath);

  await new NapCatShell(
    wrapper,
    session,
    logger,
    loginService,
    selfInfo,
    basicInfoWrapper,
    pathWrapper,
    nativePacketHandler
  ).InitNapCat();
}

export class NapCatShell {
  readonly core: NapCatCore;
  readonly context: InstanceContext;

  constructor (
    wrapper: WrapperNodeApi,
    session: NodeIQQNTWrapperSession,
    logger: LogWrapper,
    loginService: NodeIKernelLoginService,
    selfInfo: SelfInfo,
    basicInfoWrapper: QQBasicInfoWrapper,
    pathWrapper: NapCatPathWrapper,
    packetHandler: NativePacketHandler
  ) {
    this.context = {
      packetHandler,
      workingEnv: NapCatCoreWorkingEnv.Shell,
      wrapper,
      session,
      logger,
      loginService,
      basicInfoWrapper,
      pathWrapper,
    };
    this.core = new NapCatCore(this.context, selfInfo);
  }

  async InitNapCat () {
    await this.core.initCore();
    new NapCatOneBot11Adapter(this.core, this.context, this.context.pathWrapper).InitOneBot()
      .catch(e => this.context.logger.logError('初始化OneBot失败', e));
  }
}
